﻿// Nail.AutoUpdater Library
// Nail framework
//
// Copyright © Frank Nagl, 2009
// admin@franknagl.de
// www.franknagl.de
//
using System.Drawing;
using System.Drawing.Imaging;
using System;
namespace Nail
{
    /// <summary>
    /// Contains methods to process 32-bit ARGB images.
    /// </summary>
    public class ARGB
    {
        /// <summary>Color channel <c>blue</c></summary>
        public const short B = 0;
        /// <summary>Color channel <c>green</c></summary>
        public const short G = 1;
        /// <summary>Color channel <c>red</c></summary>
        public const short R = 2;
        /// <summary>Transparence channel <c>alpha</c></summary>
        public const short A = 3;

        /// <summary>
        /// Calculates the x and y coordinate of the pixel from the 
        /// index of the one-dimensional byte array, which represents the image.
        /// </summary>
        /// <param name="index">The index of the one-dimensional byte array.</param>
        /// <param name="stride">The stride of the image.</param>
        /// <param name="channel">The color channel.</param>
        /// <returns></returns>
        public static System.Drawing.Point GetPoint(int index, int stride, int channel)
        {
            int y = (index - channel) / stride;
            int x = (index - channel - y * stride) / 4;//4 channels
            return new System.Drawing.Point(x, y);
        }

        /// <summary>
        /// Calculates the index of a pixel from its x and y coordinates.
        /// </summary>
        /// <param name="x">The x coordinate.</param>
        /// <param name="y">The y coordinate.</param>
        /// <param name="stride">The stride of the image.</param>
        /// <param name="channel">The color channel.</param>
        /// <returns></returns>
        public static int GetIndex(int x, int y, int stride, int channel)
        {
            int index = y * stride + x * 4 + channel;//4 channels
            return index;
        }

        /// <summary>
        /// Creates a grayscale image as  a 32 bit-ARGB bitmap.
        /// </summary>
        /// <param name="value">The value of the RGB channels.</param>
        /// <param name="width">The width.</param>
        /// <param name="height">The height.</param>
        /// <returns></returns>
        public static Bitmap GrayColorBitmap(byte value, int width, int height)
        {
            Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb);
            Rectangle rectangle = new Rectangle(0, 0, width, height);
            // lock source image
            BitmapData imageData = bitmap.LockBits(rectangle,
                                                 ImageLockMode.ReadWrite,
                                                 bitmap.PixelFormat);

            //Bitmap width * 4 (for ARGB per pixel)
            int stride = imageData.Stride;
            // Get the address of the first line.  
            IntPtr scan0 = imageData.Scan0;

            //int offset = stride - w;//No offset in ARGB images
            int arraySize = stride * imageData.Height;
            // Declare an array to hold the bytes of the bitmap
            byte[] values = new byte[arraySize];

            // Copy the values into the array.
            System.Runtime.InteropServices.Marshal.Copy(scan0, values, 0, arraySize);

            for (int i = 0; i < arraySize; i += 4)
            {
                values[i + ARGB.A] = 255;
                values[i + ARGB.R] = value;
                values[i + ARGB.G] = value;
                values[i + ARGB.B] = value;
            }

            // Copy the RGB values back to the bitmap
            System.Runtime.InteropServices.Marshal.Copy(values, 0, scan0, arraySize);

            // unlock image
            bitmap.UnlockBits(imageData);

            return bitmap;
        }

        /// <summary>
        /// Example-based processing an image with bit moving 
        /// (without using pointer and unsafe code).
        /// </summary>
        /// <param name="bitmap">The bitmap.</param>
        public static void ProcessImage(Bitmap bitmap)
        {
            Rectangle rectangle = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
            // lock source image
            BitmapData imageData = bitmap.LockBits(rectangle,
                                                 ImageLockMode.ReadWrite,
                                                 bitmap.PixelFormat);

            //Bitmap width * 4 (for ARGB per pixel)
            int stride = imageData.Stride;
            // Get the address of the first line.  
            IntPtr scan0 = imageData.Scan0;

            //int offset = stride - w;//No offset in ARGB images
            int arraySize = stride * imageData.Height;
            // Declare an array to hold the bytes of the bitmap
            byte[] values = new byte[arraySize];

            // Copy the values into the array.
            System.Runtime.InteropServices.Marshal.Copy(scan0, values, 0, arraySize);

            for (int i = 0; i < arraySize; i += 4)
            {
                byte coeff = 128;//128 == 2^7 --> statt 8 bit nur 8-7=1 bit pro Kanal
                values[i + ARGB.A] = 255;
                values[i + ARGB.R] = (byte)((values[i + ARGB.R] / coeff) * coeff);
                values[i + ARGB.G] = (byte)((values[i + ARGB.G] / coeff) * coeff);
                values[i + ARGB.B] = (byte)((values[i + ARGB.B] / coeff) * coeff);
            }

            //Some tests
            values[ARGB.GetIndex(1, 1, stride, ARGB.A)] = 255;
            values[ARGB.GetIndex(1, 1, stride, ARGB.R)] = 0;
            values[ARGB.GetIndex(1, 1, stride, ARGB.G)] = 0;
            values[ARGB.GetIndex(1, 1, stride, ARGB.B)] = 255;
            int indexRed = ARGB.GetIndex(1, 1, stride, ARGB.R);
            Point pixelLocation = ARGB.GetPoint(25, stride, ARGB.B);
            pixelLocation = ARGB.GetPoint(27, stride, ARGB.R);
            

            // Copy the RGB values back to the bitmap
            System.Runtime.InteropServices.Marshal.Copy(values, 0, scan0, arraySize);

            // unlock image
            bitmap.UnlockBits(imageData);
        }


        /// <summary>
        /// Example-based processing an image per pointer. The <paramref name="origBitmap"/>
        /// is copied to the <paramref name="SegmentedBitmap"/>.
        /// </summary>
        /// <param name="origBitmap">The orig bitmap.</param>
        /// <param name="segmentedBitmap">The segmented bitmap.</param>
        public static void ProcessImagePerPointer(Bitmap origBitmap, Bitmap segmentedBitmap)
        {
            int width = origBitmap.Width;
            int height = origBitmap.Height;
            Rectangle origRect = new Rectangle(0, 0, width, height);
            // lock source bitmap data
            BitmapData imageData = origBitmap.LockBits(
                origRect, ImageLockMode.ReadOnly, PixelFormat.Format32bppRgb);

            // image sizes
            int stride = imageData.Stride;
            int pixelSize = 4;//ARGB
            int offset = stride - width * pixelSize;

            // lock destination bitmap data
            BitmapData dstData = segmentedBitmap.LockBits(
                origRect, ImageLockMode.WriteOnly, segmentedBitmap.PixelFormat);

            // copy image
            unsafe
            {
                byte* src = (byte*)imageData.Scan0.ToPointer();// +ymin * srcStride + xmin * pixelSize;
                byte* dst = (byte*)dstData.Scan0.ToPointer();

                // for each line
                for (int y = 0; y < height - 1; y++)
                {
                    // for each pixel in line
                    for (int x = 0; x < width; x++, dst += pixelSize, src += pixelSize)
                    {
                        // copy pixel
                        *dst = *src; //  ==  dst[0] = src[0];
                        dst[1] = src[1];
                        dst[2] = src[2];
                        dst[3] = src[3];
                    }
                    //src += offset;
                    //dst += offset;
                }
            }
            // unlock destination image
            origBitmap.UnlockBits(imageData);
            segmentedBitmap.UnlockBits(dstData);
        }
    }
}
